TRY...CATCH Block
SQL Server 2005 and 2008 provide you with a powerful error handling feature in your T-SQL code: a TRY and CATCH
control of flow statement. This error handling technique is similar to
exception handling, which exists in programmability languages. TRY
defines a block of code that might raise an error. If any statement in
this block generates an error, execution immediately stops, and the
code in the CATCH block is run. The syntax of the TRY...CATCH block is as follows:
BEGIN TRY
{ sql_statement | statement_block }
END TRY
BEGIN CATCH
[ { sql_statement | statement_block } ]
END CATCH
[ ; ]
In both blocks, you can place a single statement or a group of statements in a batch or enclosed in a BEGIN...END block. A TRY block must be immediately followed by an associated CATCH block. If you include any other statements between the END TRY and BEGIN CATCH statements, SQL Server will generate a syntax error.
This
T-SQL construction has two flows: when there is an error and when there
are no errors. If there is an error in your code detected by a TRY block, control passes to the first statement in the associated CATCH block when the error arises. When the code in the CATCH block finishes, control passes to the statement immediately after the END CATCH statement. On the other hand, when there are no errors in your TRY block, SQL Server passes control to the statement immediately after the associated END CATCH statement after the last statement in the TRY block is executed. If the END CATCH
statement is the last statement in a stored procedure or trigger,
control is passed back to the statement that called the stored
procedure or fired the trigger. You can use TRY...CATCH
blocks in many transactions and objects such as stored procedures and
triggers. However, they can’t be used in a user-defined function.
One interesting thing that you should know is that a TRY...CATCH
block catches all execution errors with a severity level higher than 10
and that don’t close the connection. Also, you can nest TRY...CATCH blocks. Either a TRY or a CATCH block can contain nested TRY...CATCH
constructs, providing great flexibility to your code. However, this
construct can’t span multiple batches or multiple blocks of statements.
For example, you can’t use two BEGIN...END blocks inside a TRY...CATCH block.
The TRY...CATCH
block allows you to handle errors that occur during compilation or
statement-level recompilation of your code such as when calling an
object that doesn’t exist. In these situations, you can handle these
errors by executing the code that generates the error in a separate
batch within the TRY block. For example, you can place the code in a
stored procedure or use the sp_executesql system procedure to dynamically create your statement. These two methods allow TRY...CATCH to catch the error at a higher level of execution than the error occurrence. Within the CATCH
block, you can determine what caused the problem and get information
about the error using special T-SQL error handling functions. These
functions retrieve data from the sys.messages catalog view. The following list shows system functions you can use to obtain information about the error that caused the CATCH block to be executed:
ERROR_NUMBER(): Returns the error ID number.
ERROR_SEVERITY(): Returns the severity level of an error.
ERROR_STATE(): Returns the error state number.
ERROR_PROCEDURE():
Returns the name of the stored procedure or trigger where the error
occurred. This is very handy information when you have nested
programmability objects, as the procedures or triggers that cause an
error may not be the ones to actually handleit.
ERROR_LINE(): Returns the line number inside the routine that caused the error.
ERROR_MESSAGE():
Returns the complete text of the error message. The great thing about
this function is that the text includes the values supplied for any
arguments.
By using these functions, you can determine whether you need to use ROLLBACK in your CATCH block to roll back your transaction.
Test Day Tip
It’s
important to you review these SQL Server system functions and
understand their returns. There will be questions on the exam where
knowledge of these functions makes a difference.
Example: Handling Errors with a TRY...CATCH Block
In
this example, we will develop a stored procedure that inserts a new
credit card in the AdventureWorks2008 database. We will handle its
output using a TRY... CATCH block. Figure 5 shows the code used to create the uspInsertValidated-CreditCard_2 procedure.
You can see that this code has some important differences from the previous example, which used the @@ERROR function. After the header of the procedure, there is a TRY block with the main code of the procedure composed of INSERT and RAISERROR
statements. The normal flow of this procedure would be the insertion of
credit card information and the exhibition of a message stating that it
completed successfully.
However, an error may arise when a user executes this procedure, and that is when the CATCH block enters. You can see that the code creates three local variables: @Severity, @State, and @Message. These variables are designed to receive values from the error handling functions ERROR_SEVERITY(), ERROR_STATE(), and ERROR_MESSAGE() via a SELECT statement. Then, after the local variable values are set, a custom error message is created using a RAISERROR statement.
To test this new procedure and see how it handles errors, let’s try to insert the same credit card number twice. Figure 6 shows a script that tries to violate the unique constraint of this table.
The
output when you execute the two procedures is really interesting. The
first credit card is inserted without any error, and SQL Server shows
the successful completion message after its execution. The second
credit card has the same number as the first one, so it will raise an
error. However, instead of SQL Server showing the message and stopping
execution when the error arose, SQL Server stops execution of the INSERT statement and starts to execute the CATCH block. You can see that the error message raised is exactly what was defined in the CATCH
block. This means that you can create custom messages as outputs of
your code and control SQL Server error messages in an easy and
efficient way. Also, observe that the text of the error message brings
the arguments filled, avoiding having to inform them in the RAISERROR statement.
Exercise . Handling Errors
In this exercise, you will develop a script that creates a table and handles any possible errors using the @@ERROR function. You will then change your code to include a TRY...CATCH block. Finally, you will execute the script.
Launch
SQL Server Management Studio (SSMS), connect to the instance, open a
new query window, and change the context to the AdventureWorks2008
database.
Create a table containing a user-defined error message by executing the following code:
CREATE TABLE [dbo].[TestTable]
(
ID INT NOT NULL PRIMARY KEY
)
IF @@ERROR != 0
RAISERROR('There was a problem!', 16, 1)
ELSE
RAISERROR('Everything is OK!', 5, 1)
Verify that the table was created by executing the sp_help system procedure, as follows:
Now, let’s execute the code that creates the TestTable
table again. We know that SQL Server will return an error message since
the table already exists. But, a strange thing should happen. The
output of SQL Server should be the following:
Msg 2714, Level 16, State 6, Line 1
There is already an object named 'TestTable' in the database.
You may be asking: Why didn’t SQL Server show my custom error message? The answer is that the @@ERROR function handled the error in the analyze step. To solve this problem, let’s rewrite the code using a TRY...CATCH block, as follows:
BEGIN TRY
CREATE TABLE [dbo].[TestTable]
(
ID INT NOT NULL PRIMARY KEY
)
RAISERROR('Everything is OK!', 5, 1)
END TRY
BEGIN CATCH
RAISERROR('There was a problem!', 16, 1)
END CATCH
Execute the code again. Now SQL Server should display your custom message.